
#ifndef _TTG_STRUCTURES_UNIVERSAL_STACK_
#define _TTG_STRUCTURES_UNIVERSAL_STACK_
#include "Common/Definitions.h"

#include "vector"
#include "Common/Math.h"

namespace ttg_internal
{
	namespace structures
	{
		//Creates copy of arbitary value.
		class Copier
		{
			public:
				virtual void *copyData(void *value) = 0;
				virtual Copier *createCopy() = 0;
		};
		//Creates copy of instance of T.
		template <class T>
		class ParametrizedCopier :public Copier
		{
			public:
				virtual void *copyData(void *value)
				{ return new T(*(T *)value); }
				virtual Copier *createCopy()
				{ return new ParametrizedCopier<T>(); }
		};
	}
}

namespace ttg
{
	namespace structures
	{
		//Stack, that can contain values of different types.
		class UniversalStack
		{
			private:
				std::vector<std::pair<void *, ttg_internal::structures::Copier *>> data;
			public:
				//Creates copy of stack.
				inline UniversalStack(const UniversalStack &stack)
				{
					for (size_t i = 0; i < stack.data.size(); i++)
						this->data.push_back(std::make_pair(
							stack.data[i].second->copyData(stack.data[i].first),
							stack.data[i].second->createCopy()));
				}
				//Creates empty stack.
				inline UniversalStack()
				{ /*nothing*/ }
				//Assigns values from another stack to current.
				inline const UniversalStack &operator =(const UniversalStack &stack)
				{
					//Guard.
					if (this == &stack)
						return *this;
					//Copying.
					this->clear();
					for (size_t i = 0; i < stack.data.size(); i++)
						this->data.push_back(std::make_pair(
							stack.data[i].second->copyData(stack.data[i].first),
							stack.data[i].second->createCopy()));
					return *this;
				}
				//Adds required element to stack.
				//Note: references are not supported (only value types and pointers).
				template <class T>
				inline void push(T data)
				{
					this->data.push_back(
						std::make_pair(new T(data), new ttg_internal::structures::ParametrizedCopier<T>()));
				}
				//Removes and returns last element.
				//In "TTG_DEBUG" mode checks correctness.
				template <class T>
				inline T pop()
				{
					if (data.size() == 0)
						throw Error(Error::FATAL, "UniversalStack", "pop",
									"There aren't enough data");
					T res(*(T *)data[data.size() - 1].first);
					SAFE_DELETE(data[data.size() - 1].first);
					SAFE_DELETE(data[data.size() - 1].second);
					data.pop_back();
					return res;
				}
				//Returns true, if there exist some elements.
				inline bool isEmpty() const
				{ return data.size() == 0; }
				//Clears stack.
				inline void clear()
				{
					for (size_t i = 0; i < data.size(); i++)
					{
						SAFE_DELETE(data[i].first);
						SAFE_DELETE(data[i].second);
					}
					data.clear();
				}
				//Creates copy of current stack.
				inline UniversalStack createCopy()
				{ return UniversalStack(*this); }
		};
	}
}

#endif
